#ifndef USER_H
#define USER_H

#include <memory>
#include <mutex>

#include "sqlite3.h"

#include "Util.h"

namespace scgi
{

class Server;

struct User
{
	typedef std::shared_ptr<User> pointer;
	typedef std::map<std::string, std::string> table_type;
	typedef table_type::const_iterator const_iterator;

	unsigned int uid;
	std::string name;
	table_type table;
	
	std::string get(std::string key_) const;
	void set(std::string key_, std::string value_);
	
	const_iterator begin() const;
	const_iterator end() const;

	template <typename T_>
	void assign(T_ first_, T_ last_)
	{
		Tokenizer<'=', '&'> tokenize;
		tokenize(
			  first_
			, last_
			, [&](std::string key_, std::string value_)
			{
				URLDecoder decode;
				this->set(
					  decode(key_.begin(), key_.end())
					, decode(value_.begin(), value_.end()));
			});
	}

	std::string to_string();
	std::string get_uid_string() const;
};

class UserTable
{
private:
	Server * server_ptr;

	sqlite3 * db;
	sqlite3_stmt
		  * stmt_create
		, * stmt_select_by_uid
		, * stmt_select_by_name
		, * stmt_update
		, * stmt_remove_by_uid;

	std::mutex mutex;

	size_t try_count;

	CacheT<unsigned int, User::pointer> cache;

public:
	UserTable(size_t try_count_ = 10, size_t cache_size_ = 3);
	~UserTable();

	size_t get_cache_size();

	User::pointer create(std::string name_);
	int remove(unsigned int uid_);
	User::pointer get(unsigned int uid_);
	User::pointer get(std::string name_);
	int set(User::pointer user_);
	
	int open_database(std::string path_);
	int create_table();

	int initialize(Server * server_ptr_);

private:
	User::pointer create_record(std::string name_);
	int remove_record_by_uid(unsigned int uid_);
	User::pointer select_record_by_uid(unsigned int uid_);
	User::pointer select_record_by_name(std::string name_);
	int update_record(User::pointer user_);
};

} // scgi

#endif // USER_H
